/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.meego.common.util;

import java.lang.reflect.Field;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author TueHM
 */
public class MapUtils {

    public static void main(String[] args) {

        HashMap<String, Double> map = new HashMap<String, Double>();

        map.put("A", 99.5);
        map.put("E", 11.1);
        map.put("F", 111.1);
        map.put("B", 67.4);
        map.put("C", 67.4);
        map.put("D", 67.3);
        TreeMap<String, Double> sorted_map = sortByValue(map, true);
        sorted_map.putAll(map);

        System.out.println("results: " + sorted_map);
        TreeMap sortKey = sortByKey(map, true);
        System.out.println(sortKey);
    }

    /**
     * Returns a view of the portion of this map whose keys start with {@code regex}
     *
     * @param <V>
     * @param map The map will be split
     * @param regex the string is used to get sub map
     * @return a view of the portion of this map whose keys start with {@code regex}
     */
    public static <V> SortedMap<String, V> subMap(Map<String, V> map, String regex) {
        if (regex.endsWith(".")) {
            regex = regex.substring(0, regex.length() - 1);
        }
        SortedMap sorted = new TreeMap();
        sorted.putAll(map);
        return sorted.subMap(regex + ".", regex + "/");
    }

    /**
     * Order the map by map value
     *
     * @param <K>
     * @param <V> extends Number
     * @param map the map will be ordered
     * @param isDESC order by ASC|DESC
     * @return the map has been ordered
     *
     * Usage Example: <br />      <code>
     * HashMap<String, Double> map = new HashMap<String, Double>(); <br />
     * map.put("A", 99.5); <br />
     * map.put("E", 11.1); <br />
     * map.put("F", 111.1); <br />
     * map.put("B", 67.4); <br />
     * map.put("C", 67.4); <br />
     * map.put("D", 67.3); <br />
     * TreeMap<String, Double> sorted_map = sortByValue(map, true); <br />
     * sorted_map.putAll(map);
     * </code>
     */
    public static <K, V> TreeMap<K, V> sortByValue(Map<K, V> map, boolean isDESC) {
        if (isDESC) {
            ValueComparatorDESC bvc = new ValueComparatorDESC(map);
            return new TreeMap<K, V>(bvc);
        } else {
            ValueComparatorASC bvc = new ValueComparatorASC(map);
            return new TreeMap<K, V>(bvc);
        }
    }
    
    public static <K, V> TreeMap<K, V> sortByValue(Map<K, V> map, boolean isDESC, final String compareField, final Class<V> clazz ) throws NoSuchFieldException {
        if (isDESC) {
            ValueCompareObjectFieldDESC bvc = new ValueCompareObjectFieldDESC(map, compareField, clazz);
            return new TreeMap<K, V>(bvc);
        } else {
            ValueCompareObjectFieldASC bvc = new ValueCompareObjectFieldASC(map, compareField, clazz);
            return new TreeMap<K, V>(bvc);
        }
    }

    /**
     *  Order the map by map key
     * @param <K>
     * @param <V>
     * @param map the map will be ordered
     * @param isDESC order by ASC|DESC
     * @return the map has been ordered
     */
    public static <K, V> TreeMap<K, V> sortByKey(Map<K, V> map, boolean isDESC) {
        TreeMap result;
        if (isDESC) {
            result = new TreeMap(Collections.reverseOrder());
        } else {
            result = new TreeMap();
        }
        result.putAll(map);
        return result;
    }

    static class ValueComparatorASC<K, V> implements Comparator<K> {

        Map<K, V> base;

        public ValueComparatorASC(Map<K, V> base) {
            this.base = base;
        }

        @Override
        public int compare(K a, K b) {
            V o1 = base.get(a);
            V o2 = base.get(b);
            if (o1 instanceof Number) {
                return Double.valueOf(o1.toString()) >= Double.valueOf(o2.toString()) ? 1 : -1;
            }
            return o1.hashCode() >= o2.hashCode() ? 1 : -1;
        }
    }

    static class ValueComparatorDESC<K, V> implements Comparator<K> {

        Map<K, V> base;

        public ValueComparatorDESC(Map<K, V> base) {
            this.base = base;
        }

        @Override
        public int compare(K a, K b) {
            V o1 = base.get(a);
            V o2 = base.get(b);
            if (o1 instanceof Number) {
                return Double.valueOf(o1.toString()) >= Double.valueOf(o2.toString()) ? -1 : 1;
            }
            return o1.hashCode() >= o2.hashCode() ? -1 : 1;
        }
    }
    
    static class ValueCompareObjectFieldASC<K, V> implements Comparator<K> {

        Map<K, V> base;
        Field fields;

        public ValueCompareObjectFieldASC(Map<K, V> base, final String compareField, final Class<V> clazz) throws NoSuchFieldException {
            this.base = base;
            this.fields = clazz.getDeclaredField(compareField);
        }

        @Override
        public int compare(K a, K b) {
            V o1 = base.get(a);
            V o2 = base.get(b);
           try {
                    if (fields.getType().isAssignableFrom(Double.class)) {
                        return Double.valueOf(fields.get(o1).toString()) >= Double.valueOf(fields.get(o2).toString()) ? -1 : 1;
                    }
                    if (fields.getType().isAssignableFrom(Long.class)) {
                        return Long.valueOf(fields.get(o1).toString()) >= Long.valueOf(fields.get(o2).toString()) ? -1 : 1;
                    }
                    if (fields.getType().isAssignableFrom(Float.class)) {
                        return Float.valueOf(fields.get(o1).toString()) >= Float.valueOf(fields.get(o2).toString()) ? -1 : 1;
                    }
                    if (fields.getType().isAssignableFrom(Integer.class)) {
                        return Integer.valueOf(fields.get(o1).toString()) >= Integer.valueOf(fields.get(o2).toString()) ? -1 : 1;
                    }
                    if (fields.getType().isAssignableFrom(Short.class)) {
                        return Short.valueOf(fields.get(o1).toString()) >= Short.valueOf(fields.get(o2).toString()) ? -1 : 1;
                    }
                    return fields.get(o1).hashCode() >= fields.get(o2).hashCode() ? -1 : 1;
                } catch (Exception ex) {
                    Logger.getLogger(ListUtils.class.getName()).log(Level.SEVERE, null, ex);
                }
                return 0;
        }
    }

    static class ValueCompareObjectFieldDESC<K, V> implements Comparator<K> {

        Map<K, V> base;
        Field fields;

        public ValueCompareObjectFieldDESC(Map<K, V> base, final String compareField, final Class<V> clazz) throws NoSuchFieldException {
            this.base = base;
            this.fields = clazz.getDeclaredField(compareField);
        }

        @Override
        public int compare(K a, K b) {
            V o1 = base.get(a);
            V o2 = base.get(b);
           try {
                    if (fields.getType().isAssignableFrom(Double.class)) {
                        return Double.valueOf(fields.get(o1).toString()) <= Double.valueOf(fields.get(o2).toString()) ? -1 : 1;
                    }
                    if (fields.getType().isAssignableFrom(Long.class)) {
                        return Long.valueOf(fields.get(o1).toString()) <= Long.valueOf(fields.get(o2).toString()) ? -1 : 1;
                    }
                    if (fields.getType().isAssignableFrom(Float.class)) {
                        return Float.valueOf(fields.get(o1).toString()) <= Float.valueOf(fields.get(o2).toString()) ? -1 : 1;
                    }
                    if (fields.getType().isAssignableFrom(Integer.class)) {
                        return Integer.valueOf(fields.get(o1).toString()) <= Integer.valueOf(fields.get(o2).toString()) ? -1 : 1;
                    }
                    if (fields.getType().isAssignableFrom(Short.class)) {
                        return Short.valueOf(fields.get(o1).toString()) <= Short.valueOf(fields.get(o2).toString()) ? -1 : 1;
                    }
                    return fields.get(o1).hashCode() <= fields.get(o2).hashCode() ? -1 : 1;
                } catch (Exception ex) {
                    Logger.getLogger(ListUtils.class.getName()).log(Level.SEVERE, null, ex);
                }
                return 0;
        }
    }
}
